/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/////////////////////////////////////////////////////////////////////////////
///
/// Example openharmony Application/Ability that allows processing WAV
/// audio files with SoundTouch library
///
////////////////////////////////////////////////////////////////////////////////

package net.surina.soundtouch.slice;

import net.surina.soundtouch.ResourceTable;
import net.surina.soundtouch.SoundTouch;
import net.surina.soundtouch.util.LogUtil;
import net.surina.soundtouch.util.ResUtil;

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;

import ohos.agp.components.Button;
import ohos.agp.components.Checkbox;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import ohos.agp.utils.Color;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

public class MainAbilitySlice extends AbilitySlice {

    Text textViewConsole = null;
    TextField editSourceFile = null;
    TextField editOutputFile = null;
    TextField editTempo = null;
    TextField editPitch = null;
    Checkbox checkBoxPlay = null;

    StringBuilder consoleText = new StringBuilder();

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        textViewConsole = (Text) findComponentById(ResourceTable.Id_txtResult);
        editSourceFile = (TextField) findComponentById(ResourceTable.Id_fieldSrcFileName);
        editOutputFile = (TextField) findComponentById(ResourceTable.Id_fieldDstFileName);

        editTempo = (TextField) findComponentById(ResourceTable.Id_fieldTempo);
        editPitch = (TextField) findComponentById(ResourceTable.Id_fieldPitch);
        editTempo.setBackground(ResUtil.buildDrawableByColor(Color.LTGRAY.getValue()));
        editPitch.setBackground(ResUtil.buildDrawableByColor(Color.LTGRAY.getValue()));

        Button buttonFileSrc = (Button) findComponentById(ResourceTable.Id_btnSelectSrcFile);
        Button buttonFileOutput = (Button) findComponentById(ResourceTable.Id_btnSelectOutputFile);
        Button buttonProcess = (Button) findComponentById(ResourceTable.Id_btnProcess);
        buttonFileSrc.setClickedListener(component -> showWarning());
        buttonFileOutput.setClickedListener(component -> showWarning());
        buttonProcess.setClickedListener(component -> process());

        checkBoxPlay = (Checkbox) findComponentById(ResourceTable.Id_checkBoxPlay);
        checkBoxPlay.setBackground(ResUtil.buildDrawableByColor(Color.LTGRAY.getValue()));
        checkLibVersion();
    }

    private void showWarning() {
        ResUtil.showToast(getContext(), "File selector not implemented, sorry! Enter the file path manually ;-)", 1500);
    }

    /// Function to append status text onto "console box" on the Ability
    public void appendToConsole(final String text) {
        // run on UI thread to avoid conflicts
        getUITaskDispatcher().asyncDispatch(() -> {
            consoleText.append(text);
            consoleText.append("\n");
            textViewConsole.setText(String.valueOf(consoleText));
        });
    }

    /// print SoundTouch native library version onto console
    protected void checkLibVersion() {
        String ver = SoundTouch.getVersionString();
        appendToConsole("SoundTouch native library version = " + ver);
    }

    /// Play audio file
    private void playWavFile(String outFileName) {
        present(new AudioClientSlice(), new Intent());
    }

    /// Helper class that will execute the SoundTouch processing. As the processing may take
    /// some time, run it in background thread to avoid hanging of the UI.
    private class ProcessTask extends EventHandler {

        private static final int MSG_PARAM = 0;

        /// Helper class to store the SoundTouch file processing parameters
        public final class Parameters {
            String inFileName;
            String outFileName;
            float tempo;
            float pitch;
        }

        /// Function that does the SoundTouch processing
        public final long doSoundTouchProcessing(Parameters params) {

            SoundTouch st = SoundTouch.Instance();
            st.setTempo(params.tempo);
            st.setPitchSemiTones(params.pitch);
            LogUtil.info("SoundTouch", "process file " + params.inFileName);
            long startTime = System.currentTimeMillis();
            int res = st.processFile(params.inFileName, params.outFileName);
            long endTime = System.currentTimeMillis();
            float duration = (endTime - startTime) * 0.001f;

            LogUtil.info("SoundTouch", "process file done, duration = " + duration);
            appendToConsole("Processing done, duration " + duration + " sec.");
            if (res != 0) {
                String err = SoundTouch.getErrorString();
                appendToConsole("Failure: " + err);
                return -1L;
            }

            // Play file if so is desirable
            if (checkBoxPlay.isChecked()) {
                playWavFile(params.outFileName);
            }
            return 0L;
        }

        public ProcessTask(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        public void execute(Parameters parameters) {
            sendEvent(InnerEvent.get(MSG_PARAM, 0, parameters));
        }

        @Override
        public void processEvent(InnerEvent event) {
            int what = event.eventId;

            switch (what) {
                case MSG_PARAM:
                    Parameters parameters = (Parameters) event.object;
                    doSoundTouchProcessing(parameters);
                    break;
                default:
                    throw new RuntimeException("unknown message " + what);
            }
        }

    }

    /// process a file with SoundTouch. Do the processing using a background processing
    /// task to avoid hanging of the UI
    protected void process() {
        try {
            ProcessTask task = new ProcessTask(EventRunner.create());
            ProcessTask.Parameters params = task.new Parameters();
            // parse processing parameters
            params.inFileName = ResUtil.getAudioInputFilePath(getContext());
            params.outFileName = editOutputFile.getText().toString();
            params.tempo = 0.01f * Float.parseFloat(editTempo.getText().toString());
            params.pitch = Float.parseFloat(editPitch.getText().toString());

            // update UI about status
            appendToConsole("Process audio file :" + params.inFileName + " => " + params.outFileName);
            appendToConsole("Tempo = " + params.tempo);
            appendToConsole("Pitch adjust = " + params.pitch);

            ResUtil.showToast(this, "Starting to process file " + params.inFileName + "...", 1500);

            // start SoundTouch processing in a background thread
            task.execute(params);

        } catch (Exception exp) {
            exp.printStackTrace();
        }
    }
}
